#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>

/*接受http服务器，解析http服务器，响应http请求*/


#define SERVER_PORT 80

static int debug=1;

int get_line(int sock,char*buf,int size);   //接受http请求，实现按行读取请求头部
void* do_http_request(void* client_sock);   //解析http请求
void do_http_response(int client_sock,const char *path);    //响应http请求
void not_found(int client_sock);                            //404 请求内容不存在
int headers(int client_sock,FILE *resource);                //读取头部信息
void cat(int client_sock,FILE *resource);                   //读取身体信息
void inner_error(int client_sock);                          //send reply failed
void unimplemented(int client_sock);                        //500 Internal Server Error
void bad_request(int client_sock);                          //400 客户端发送的请求格式有问题等


int main(void){

    int sock;//代表信箱
    struct sockaddr_in server_addr;


    //创建信箱
    sock = socket(AF_INET, SOCK_STREAM, 0);//创建TCP套接字

    //2.清空标签，写上地址和端口号
    bzero(&server_addr, sizeof(server_addr));

    server_addr.sin_family = AF_INET;//选择协议族IPV4
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本地所有IP地址
    server_addr.sin_port = htons(SERVER_PORT);//绑定端口号

    //实现标签贴到收信得信箱上
    bind(sock, (struct sockaddr *)&server_addr,  sizeof(server_addr));

    //把信箱挂置到传达室，这样，就可以接收信件了
    listen(sock, 128);  //启动监听


    printf("等待客户端的连接\n");


    int done =1;

    while(done)
    {
        struct sockaddr_in client;
        int client_sock, len, i;
        char client_ip[64];
        char buf[256];
        pthread_t id;
        int pclient_sock=NULL;


        socklen_t  client_addr_len;
        client_addr_len = sizeof(client);
        client_sock = accept(sock, (struct sockaddr *)&client, &client_addr_len);

       // printf("client_sock: %s\n", client_sock);锚点


        //打印客服端IP地址和端口号
        printf("client ip: %s\t port : %d\n",
                 inet_ntop(AF_INET, &client.sin_addr.s_addr,client_ip,sizeof(client_ip)),
                 ntohs(client.sin_port));
        
        //处理http请求，读取客户端发出的数据

        //do_http_request(client_sock);

        pclient_sock=(int*)malloc(sizeof(int));
        *pclient_sock=client_sock;


        //启动线程处理http请求
        pthread_create(&id,NULL,do_http_request,(void*)pclient_sock);

        //close(client_sock);
    }
    close(sock);
    return 0;
}

void* do_http_request(void* pclient_sock)
 {
    int  len=0;                 //一行的长度
    char buf[256];              
    char method[64];
    char url[256];
    char path[256];
    int client_sock=*(int *)pclient_sock;

    struct stat st;
    /*读取客户端发送的http请求*/

    //1.读取请求行（就是第一行）    一次读一行，读出的数据存入buf，一行的长度
    len = get_line(client_sock, buf, sizeof(buf));  //接受http请求
   
    if(len>0)                   //读到了请求行
    {
       int i=0, j=0;
       while(!isspace(buf[j]) && ( i< sizeof(method)-1))    //isspace判断字符是否是白空格   i表示已读字符串的下标
       {
           method[i]=buf[j];
           i++;
           j++;
       }
       method[i]='\0';

       if(debug)
            printf("request method:%s\n",method);


       if(strncasecmp(method, "GET",i)==0)//只处理get请求  i就是3或者写成sizeof(method)
       {//GET 方法
            if(debug)
                printf("method=GET\n");

            //获取url，把url字段读进数组
            while(isspace(buf[j++]));//跳过空白格,如果是白空格则只执行j++操作
            i=0;

            while(!isspace(buf[j]) && ( i< sizeof(url)-1))
            {
                url[i]=buf[j];
                i++;
                j++;
            }

            url[i]='\0';

            if(debug)
                printf("url:%s\n",url);

                                            //继续读取http头部
           do{

                len = get_line(client_sock, buf, sizeof(buf));
                printf("read line: %s\n", buf);

            }while(len > 0);

        //***定义本地的html文件***


        //---------------------------------------------------------
            //处理url中的
            char *pos=strchr(url,'?');//返回首次出现？的位置的指针，如果不存在c则返回NULL
            if(pos)
            {
                *pos='\0';
                printf("real url: %s\n",url);
            }
        //-------------------------------------------------------------
            sprintf(path,"./html_docs/%s",url);

            if(debug)
                printf("path:%s\n",path);

            //执行http相应
            //判断文件是否存在，如果存在就响应200 ok，同时发送相应的HTML文件，如果不存在就响应404 not found
            if(stat(path,&st)==-1)      //文件不存在或者存错
            {
                fprintf(stderr,"stat %s failed.reason:%s\n",path,strerror(errno));
                not_found(client_sock);
            }
            else                        //文件存在
            {

                if(S_ISDIR(st.st_mode))     //如果是一个目录
                {
                    strcat(path,"/index.html");  //就在目录里追加一个index.html
                }
                do_http_response(client_sock,path);
            }
        }
        else             //非get请求，读取http头部，并响应客户端501（Method Not Implemented）
        {
            fprintf(stderr,"warning!other request [%s]\n",method);

            do{
                  len = get_line(client_sock, buf, sizeof(buf));
                     printf("read line: %s\n", buf);
             }while(len > 0);

             unimplemented(client_sock);//请求未实现

        }
    }
    else
    {
        bad_request(client_sock);       //在响应时再实现
    }

    close(client_sock);
    if(pclient_sock) free(pclient_sock);//释放动态分配的内存
    
    return NULL;
}

void do_http_response(int client_sock,const char *path)
{
    FILE *resource = NULL;
    int ret=0;//判断头部是否发送成功
    resource = fopen(path,"r");

    if(resource==NULL)
    {
        not_found(client_sock);
        return;
    }

    //发送http头部
    ret=headers(client_sock,resource);

    //发送http body
    if(!ret)
    {
        cat(client_sock,resource);
    }
    

    fclose(resource);
}

int headers(int client_sock,FILE *resource)
{
    struct stat st;
    int fileid=0;
    char tmp[64];
    char buf[1024]={0};
    strcpy(buf,"HTTP/1.0 200 OK\r\n");
    strcpy(buf,"Server:Chunyu123 Server\r\n");
    strcpy(buf,"Content-Type:text/html\r\n");
    strcpy(buf,"Connection:Close\r\n");

    filed=fileno(resource);

    if(fstat(fileid,&st)==-1)           //服务器内部出错
    {
        inner_error(client_sock);
        return -1;
    }

    snprintf(tmp,64,"Content-Length:%ld\r\n\r\n",st.st_size);
    strcat(buf,tmp);

    if(debug)  fprintf(stdout,"header: %s\n",st.st_size);

    if(send(client_sock,buf,strlen(buf),0)<0);//往sock上送消息  发送失败
    {
        fprintf(stderr,"send failed.data: %s\n,reason:%s\n",buf,strerror(errno));
    }

    return 0;
}

void cat(int client_sock,FILE *resource)
{
    char buf[1024];

    fgets(buf,sizeof(buf),resource);

    while(!feof(resource))
    {
        int len=write(client_sock,buf,strlen(buf));

        if(len<0)
        {
            fprintf(stderr,"send failed.data: %s\n,reason:%s\n",buf,strerror(errno));
            break;
        }

        if(debug)  fprintf(stdout,"cat: %s\n",st.st_size);
    }



}

void do_http_response1(int client_sock)
{
    const char *main_header="HTTP/1.0 200 OK\r\n
Server:Chunyu123 Server\r\n
Content-Type:text/html\r\n
Connection:Close\r\n";

    const char *welcome_content="<html lang=\"zh-CN\">\n\
<head>\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\
<title>This is a test</title>\n\
</head>\n\
<body>\n\
<div align=center height=\"500px\" >\n\
<br/><br/><br/>\n\
<h2>大家好，我是卢云春！</h2><br/><br/>\n\
<form action=\"commit\" method=\"post\">\n\
尊姓大名: <input type=\"text\" name=\"name\" />\n\
<br/>芳龄几何: <input type=\"password\" name=\"age\" />\n\
<br/><br/><br/><input type=\"submit\" value=\"提交\" />\n\
<input type=\"reset\" value=\"重置\" />\n\
</form>\n\
</div>\n\
</body>\n\
</html>\n";

    //1.送main_header
    int len=write(client_sock,main_header,strlen(main_header));

    if(debug)  fprintf(stdout,"...do_http_response...\n");
    if(debug)  fprintf(stdout,"write[%d]: %s",len,main_header);


    //2.生成Content-Length 行并发送
    char send_buf[64];
    int wc_len=strlen(welcome_content);
    len=snprintf(send_buf,64,"Content-Length: %d\r\n\r\n",wc_len);
    len=write(client_sock,send_buf,len);

    if(debug) fprintf(stdout,"write[%d]: %s",len,send_buf);

    len=write(client_sock,welcome_content,wc_len);
    if(debug) fprintf(stdout,"write[%d]: %s",len,welcome_content);

    //3.发送html文件内容


}

int get_line(int sock,char*buf,int size)
{
    //返回值： -1 表示读取出错， 等于 0 表示读到一个空行， 大于 0 表示成功读取一行

    int count=0;        //计数器   已经读取的字符个数
    char ch='\0';       //字符串结束符
    int len=0;          //目前已读的长度
    
    while((count<size-1)&&ch!='\n')
    {
        len=read(sock,&ch,1);   //只读一个字符
        if(len==1)              //成功读到一个字符
        {
            if(ch=='\r')        //ch为回车，结束0
                continue;
            else if(ch='\n')    //ch为换行
            {
                buf[count]='\0';//字符串结束符
                break;
            }
                                //ch为正常字符
            buf[count]=ch;
            count++;            
        }
        else if(len==-1)        //读取出错
        {
            perror("read failed");
            count==-1;          //count=-1表示读取出错，=0表示读到空行，>0表示成功读取到一行
            break;
        }
        else                    //read返回0，客户端返回sock连接
        {
            fprintf(stderr,"client close.\n");
            count=-1;
            break;
        }
    }
    if(count>=0)
        buf[count]='\0';       

    return count;
}

void not_found(int client_sock)
{
    const char *reply="HTTP/1.0 404 NOT FOUND\r\n
Content-Type: text/html\r\n
\r\n
<HTML>\r\n
<HEAD>\r\n
<TITLE>NOT FOUND</TITLE>\r\n
</HEAD>\r\n
<BODY>\r\n
    <P>The server could not fulfill your request because the resource specified is unavailable or nonexistent.\r\n
</BODY>\r\n
</HTML>";

    int len=write(client_sock,reply,strlen(reply));
    if(debug) fprintf(stdout,reply);

    if(len<=0)
    {
        fprintf(stderr,"send reply failed.reason:%s\n",strerror(errno));
    }
}

void inner_error(int client_sock)
{
    const char *reply="HTTP/1.0 500 Internal Sever Error\r\nContent-Type: text/html\r\n\r\n<HTML>\r\n<HEAD>\r\n<TITLE>Inner Error</TITLE>\r\n</HEAD>\r\n<BODY>\r\n<P>服务器内部出错.\r\n</BODY>HTTP\r\n</HTML>";

    int len=write(client_sock,reply,strlen(reply));
    if(debug) fprintf(stdout,reply);

    if(len<=0)
    {
        fprintf(stderr,"send reply failed.reason:%s\n",strerror(errno));
    }
}

void unimplemented(int client_sock)
{
    const char *reply="HTTP/1.0 501 Method Not Implemented\r\nContent-Type: text/html\r\n\r\n<HTML>\r\n<HEAD>\r\n<TITLE>Method Not Implemented</TITLE>\r\n</HEAD>\r\n<BODY>\r\n<P>HTTP request method not supported.\r\n</BODY>\r\n</HTML>\r\n";

    int len=write(client_sock,reply,strlen(reply));
    if(debug) fprintf(stdout,reply);

    if(len<=0)
    {
        fprintf(stderr,"send reply failed.reason:%s\n",strerror(errno));
    }
}

void bad_request(int client_sock)
{
    const char *reply="HTTP/1.0 400 BAD REQUEST\r\nContent-Type: text/html\r\n\r\n<HTML>\r\n<HEAD>\r\n<TITLE>BAD REQUEST</TITLE>\r\n</HEAD>\r\n<BODY>\r\n<P>Your browser sent a bad request\r\n</BODY>\r\n</HTML>\r\n";

    int len=write(client_sock,reply,strlen(reply));
    if(debug) fprintf(stdout,reply);

    if(len<=0)
    {
        fprintf(stderr,"send reply failed.reason:%s\n",strerror(errno));
    }

}

